
package statcomponent;

import cn.hutool.core.util.IdUtil;
import constants.NumberConstants;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import statcomponent.demo.StatResult;
import statcomponent.demo.Student;
import statcomponent.rules.DoubleStatRule;
import statcomponent.rules.IntegerStatRule;
import utils.DoubleUtils;

import java.io.Serializable;
import java.util.List;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * 统计执行器
 *
 * @author chenfoxin
 * @date 2022年09月01日 9:11
 */
public class StatManager implements Serializable {
    private static final Logger logger = LoggerFactory.getLogger(StatManager.class);

    /**
     * 统计执行
     * @param statList
     * @param statContext
     * @author chenfoxin
     * @date: 2022/9/1 9:42
     * @return java.util.Map<String,Object>
     */
    public static <T,R,U,R1> void execute(List<T> statList, StatContext<U> statContext){
        if(CollectionUtils.isEmpty(statList)){
            logger.info("stat#execute#nodata#");
            return;
        }
        if(Objects.isNull(statContext) || CollectionUtils.isEmpty(statContext.getAttributes())){
            logger.info("stat#execute#noStatAttributeManager or noStatAttributes");
            return;
        }
        //获取需要统计的属性
        List<StatAttribute> statAttributes = statContext.getAttributes();
        //获取当前traceId
        String executeId = IdUtil.fastSimpleUUID();
        for(T data : statList){
            try {
                if(Objects.isNull(data)){
                    continue;
                }
                for(StatAttribute<T,R,U,R1> statAttribute : statAttributes){
                    if(!checkParam(statAttribute)) {
                        continue;
                    }
                    //条件不成立，也不统计
                    Predicate<T> predicate = statAttribute.getPredicate();
                    if(Objects.nonNull(predicate) && !predicate.test(data)){
                        continue;
                    }
                    Function<T,R> function = statAttribute.getFunction();
                    //获取属性
                    R val = function.apply(data);
                    //统计属性
                    AbstractStatRule<R,R1> abstractStatRule = statAttribute.getAbstractStatRule();
                    abstractStatRule.doStat(val);
                }
            }catch (Exception e){
                logger.info("ConsolidationManager#execute#执行失败#{}",e.getMessage(),e);
            }
        }
        U statResult = statContext.getResultRecord();
        //设置统计值
        for(StatAttribute<T,R,U,R1> attribute : statAttributes){
            AbstractStatRule<R,R1> abstractStatRule = attribute.getAbstractStatRule();
            //获取处理结果
            R1 value = abstractStatRule.result();
            //获取处理函数
            BiConsumer<U,R1> biConsumer = attribute.getResultProcessConsumer();
            //设置处理结果
            biConsumer.accept(statResult,value);
        }
        logger.info("执行id={}#统计结果={}",executeId,statResult);
    }

    /**
     * @author chenfoxin
     * @description 校验方法特殊参数
     * @param statAttribute
     * @date: 2023/7/29 15:39
     * @return boolean
     */
    private static <U, T, R1, R> boolean checkParam(StatAttribute<T,R,U,R1> statAttribute) {
       if(Objects.isNull(statAttribute.getFunction())) {
           throw new RuntimeException("获取方法缺失");
       }
        if(Objects.isNull(statAttribute.getAbstractStatRule())) {
            throw new RuntimeException("计算规则缺失");
        }
        if(Objects.isNull(statAttribute.getResultProcessConsumer())) {
            throw new RuntimeException("设置结果方法缺失");
        }
        return true;
    }

    /**
     * 构建抽象mode
     * @param attributes
     * @author chenfoxin
     * @date: 2022/9/1 9:42
     * @return AbstractConsolidationMode
     */
    public static StatContext newStatContext(StatAttribute... attributes){
        return new StatContext(attributes);
    }

    /**
     * 统计年龄、统计分数
     * @author chenfoxin
     * @date: 2022/9/1 17:40
     * @return AbstractConsolidationMode
     * @param statResult
     */
    public static StatContext<StatResult> buildStatStudentContext(StatResult statResult){
        return new StatContext<>(
                statResult,
                //统计年龄
                new StatAttribute<>(Student::getAge, new IntegerStatRule(), StatResult::setAge),
                //统计分数
                new StatAttribute<>(Student::getClassScore, new DoubleStatRule(NumberConstants.INT_TWO), StatResult::setScore),
                //统计数学+语文成绩(组合字段)
                new StatAttribute<>((Function<Student, Double>) student -> DoubleUtils.add(NumberConstants.INT_TWO,
                        student.getChinese(),student.getMath()), new DoubleStatRule(NumberConstants.INT_TWO),
                        StatResult::setMathChinese));

    }


}